package pay.util;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;

import base.util.PropertiesContent;
import mt.Const;

/**
 * @Description 支付宝对接相关验证工具
 * @Class com.unibroad.touchus.oss.biz.apay.utils.ApayVerifyUtils
 * @author suan@unibroad.com
 * @version V1.0
 */
public class ApayVerifyUtils {

    private static final Logger LOG = Logger.getLogger(ApayVerifyUtils.class);

    /**
     * 支付宝消息验证地址
     */
    private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";

    /**
     * 应用网关调用消息来源验签
     * 
     * @param params
     * @throws AlipayApiException
     */
    public static boolean verifySign(Map<String, String> params) throws AlipayApiException {
        return AlipaySignature.rsaCheckV2(params, PropertiesContent.get(Const.ALIPAY_PUBLIC_KEY_CFG),
                PropertiesContent.get(Const.ALIPAY_MSG_CHARSET_CFG));
    }

    /**
     * 验证消息是否是支付宝发出的合法消息
     * 
     * @param params
     *            通知返回来的参数数组
     * @return 验证结果
     */
    public static boolean verifyNotify(Map<String, String> params) {
        // 判断responsetTxt是否为true，isSign是否为true
        // responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
        // isSign不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
        String responseTxt = "true";
        if (params.get(Const.ALIPAY_MSG_NOTIFY_ID) != null) {
            String notify_id = params.get(Const.ALIPAY_MSG_NOTIFY_ID);
            responseTxt = verifyResponse(notify_id);
        }
        // 这里只验证调用来源支付宝就OK,不再验签
        // String sign = "";
        // if (params.get(SysConstants.ALIPAY_MSG_SIGN) != null) {
        // sign = params.get(SysConstants.ALIPAY_MSG_SIGN);
        // }
        boolean isSign = true; // getRSASignVerify(params, sign);
        if (isSign && responseTxt.equals("true")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 除去数组中的空值和签名参数
     * 
     * @param sArray
     *            签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, String> reqParamsFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase(Const.ALIPAY_MSG_SIGN)
                    || key.equalsIgnoreCase(Const.ALIPAY_MSG_SIGN_TYPE)) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * 
     * @param params
     *            需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);

            if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

    /**
     * 根据反馈回来的信息，生成签名结果
     * 
     * @param Params
     *            通知返回来的参数数组
     * @param sign
     *            比对的签名结果
     * @return 生成的签名结果
     */
    @SuppressWarnings("unused")
    private static boolean getRSASignVerify(Map<String, String> params, String sign) {
        // 过滤空值、sign与sign_type参数
        Map<String, String> sParaNew = reqParamsFilter(params);
        // 获取待签名字符串
        String preSignStr = createLinkString(sParaNew);
        // 获得签名验证结果
        boolean isSign = false;
        try {
            isSign = AlipaySignature.rsaCheckContent(preSignStr, sign,
                    PropertiesContent.get(Const.ALIPAY_PUBLIC_KEY_CFG),
                    PropertiesContent.get(Const.ALIPAY_MSG_CHARSET_CFG));

        } catch (AlipayApiException e) {
            LOG.error("getRSASignVerify exception", e);
        }
        return isSign;
    }

    /**
     * 获取远程服务器ATN结果,验证返回URL
     * 
     * @param notify_id
     *            通知校验ID
     * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true 返回正确信息 false
     *         请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    private static String verifyResponse(String notify_id) {
        // 获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求
        String veryfy_url = HTTPS_VERIFY_URL + "partner=" + PropertiesContent.get(Const.ALIPAY_PID_CFG)
                + "&notify_id=" + notify_id;
        return checkUrl(veryfy_url);
    }

    /**
     * 获取远程服务器ATN结果
     * 
     * @param urlvalue
     *            指定URL路径地址
     * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true 返回正确信息 false
     *         请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    private static String checkUrl(String urlvalue) {
        String inputLine = "";
        try {
            URL url = new URL(urlvalue);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            inputLine = in.readLine().toString();
        } catch (Exception e) {
            LOG.error("check apay notify url failure,url=" + urlvalue, e);
            inputLine = "";
        }
        return inputLine;
    }
}
